Skip to content

Compact GSplat work buffer format#8480

Merged
mvaligursky merged 3 commits intomainfrom
mv-compact-work-buffer-format
Feb 26, 2026
Merged

Compact GSplat work buffer format#8480
mvaligursky merged 3 commits intomainfrom
mv-compact-work-buffer-format

Conversation

@mvaligursky
Copy link
Contributor

@mvaligursky mvaligursky commented Feb 25, 2026

Adds a compact 20 bytes/splat work buffer format (GSPLATDATA_COMPACT) as the new default,
reducing memory and bandwidth by 37.5% compared to the existing 32 bytes/splat format.

  • Compact format layout: R32U color (11+11+10 bit RGB, [0,4] range), RGBA32U transform
    (f32 position + half-angle quaternion), R32U scale/alpha (3x8-bit log-encoded scale + 8-bit alpha)
  • Half-angle quaternion projection for rotation: branchless decode, no index bits overhead
  • Log-encoded scale covering e^-12 to e^9 range
  • Switchable at runtime via scene.gsplat.dataFormat property
  • Unified instanced draw path for all splats (octree and non-octree), removing the
    zero-fill code path and redundant uniforms
  • Format-specific writeSplat() shader interface decoupling the copy shader from
    individual format encodings
  • Compact toggle added to viewer and lod-streaming examples

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a compact work buffer format for GSplat rendering that reduces memory and bandwidth usage by 37.5% (from 32 to 20 bytes per splat) compared to the existing format. The compact format uses advanced encoding techniques including 11+11+10 bit RGB color (supporting [0,4] range), half-angle quaternion projection for rotation (3 components instead of 4), and log-encoded scale values covering e^-12 to e^9 range. The format is runtime-switchable via the scene.gsplat.dataFormat property, with GSPLATDATA_COMPACT as the new default and GSPLATDATA_LARGE available for full precision when needed.

The PR also unifies the rendering code path by using instanced draws for all splats (both octree and non-octree), eliminating the zero-fill code path that was previously needed for non-LOD splats. This simplification removes redundant uniforms (uActiveSplats, uViewportWidth) and ensures consistent behavior across all rendering scenarios.

Changes:

  • Added compact 20 bytes/splat format with specialized encoding (half-angle quaternion, log-encoded scale, packed RGB)
  • Created format-specific read/write shader interfaces for both GLSL and WGSL
  • Unified instanced draw path for all splats, removing conditional rendering logic
  • Added runtime format switching capability with automatic work buffer recreation
  • Updated viewer and lod-streaming examples with compact format toggle (defaulting to enabled)

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/scene/constants.js Added GSPLATDATA_COMPACT and GSPLATDATA_LARGE constants for format selection
src/scene/gsplat-unified/gsplat-params.js Implemented format creation and runtime switching logic, defaulting to compact format
src/scene/gsplat-unified/gsplat-manager.js Added handleFormatChange() to detect and handle format changes, ensuring work buffer recreation
src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js Removed conditional intervals logic, unified to always use instanced draws
src/scene/gsplat-unified/gsplat-renderer.js Extracted setOrderData() method for reuse after work buffer changes
src/scene/gsplat-unified/gsplat-info.js Synthesizes full-range interval when none exist to ensure unified instanced path
src/scene/gsplat/gsplat-format.js Added setWriteCode/getWriteCode methods for format-specific write encoding
src/scene/gsplat/gsplat-resource-base.js Integrated format-specific write code injection, removed useIntervals parameter
src/scene/shader-lib/wgsl/chunks/gsplat/vert/formats/containerCompactRead.js WGSL read functions for compact format with half-angle quaternion and log-scale decoding
src/scene/shader-lib/wgsl/chunks/gsplat/frag/formats/containerCompactWrite.js WGSL write functions for compact format with specialized encoding
src/scene/shader-lib/wgsl/chunks/gsplat/frag/formats/containerPackedWrite.js WGSL write functions for large format, extracted from copy shader
src/scene/shader-lib/wgsl/chunks/gsplat/frag/gsplatCopyToWorkbuffer.js Unified shader logic, removed zero-fill path, uses format-specific writeSplat()
src/scene/shader-lib/glsl/chunks/gsplat/vert/formats/containerCompactRead.js GLSL read functions for compact format (parallel to WGSL version)
src/scene/shader-lib/glsl/chunks/gsplat/frag/formats/containerCompactWrite.js GLSL write functions for compact format (parallel to WGSL version)
src/scene/shader-lib/glsl/chunks/gsplat/frag/formats/containerPackedWrite.js GLSL write functions for large format, extracted from copy shader
src/scene/shader-lib/glsl/chunks/gsplat/frag/gsplatCopyToWorkbuffer.js Unified shader logic (parallel to WGSL version)
src/scene/shader-lib/wgsl/collections/gsplat-chunks-wgsl.js Removed containerPackedRead from collection (now imported directly where needed)
src/scene/shader-lib/glsl/collections/gsplat-chunks-glsl.js Removed containerPackedRead from collection (now imported directly where needed)
examples/src/examples/gaussian-splatting/viewer.example.mjs Added compact toggle control, defaults to enabled
examples/src/examples/gaussian-splatting/viewer.controls.mjs Added UI control for compact format toggle
examples/src/examples/gaussian-splatting/lod-streaming.example.mjs Added compact toggle control, defaults to enabled
examples/src/examples/gaussian-splatting/lod-streaming.controls.mjs Added UI control for compact format toggle

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mvaligursky mvaligursky requested a review from slimbuck February 25, 2026 17:18
@mvaligursky mvaligursky merged commit 7822448 into main Feb 26, 2026
8 checks passed
@mvaligursky mvaligursky deleted the mv-compact-work-buffer-format branch February 26, 2026 09:43
mvaligursky pushed a commit that referenced this pull request Feb 26, 2026
setLayout synthesized a full-range interval [0, activeSplats) directly
into this.intervals when no intervals existed (fully-loaded octree).
This mutation leaked into GSplatIntervalCompaction.uploadIntervals,
which saw intervals.length > 0 and fell into the wrong branch —
mapping all splats to a single boundsIndex and losing per-node culling
granularity. The result was incorrect GPU frustum culling when all
octree nodes were loaded.

Move the synthesis into updateSubDraws as a local variable so
this.intervals is never mutated and the interval compaction correctly
falls back to placementIntervals for per-node bounds mapping.

Fixes regression from #8480.

Made-with: Cursor
mvaligursky added a commit that referenced this pull request Feb 26, 2026
setLayout synthesized a full-range interval [0, activeSplats) directly
into this.intervals when no intervals existed (fully-loaded octree).
This mutation leaked into GSplatIntervalCompaction.uploadIntervals,
which saw intervals.length > 0 and fell into the wrong branch —
mapping all splats to a single boundsIndex and losing per-node culling
granularity. The result was incorrect GPU frustum culling when all
octree nodes were loaded.

Move the synthesis into updateSubDraws as a local variable so
this.intervals is never mutated and the interval compaction correctly
falls back to placementIntervals for per-node bounds mapping.

Fixes regression from #8480.

Made-with: Cursor

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: graphics Graphics related issue performance Relating to load times or frame rate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants